package zombiegame;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
 * Simulator for Midterm Zombiegame.
 * @author pylaffon
 *
 */
public class Simulator {
    // Constants representing configuration information for the simulation.
    // The default width for the grid.
    private static final int DEFAULT_WIDTH = 50;
    // The default depth of the grid.
    private static final int DEFAULT_DEPTH = 50;
    // The probability that a vampire will be created in any given grid position.
    private static final double VAMPIRE_CREATION_PROBABILITY = 0.02;
    // The probability that a zombie will be created in any given grid position.
    private static final double ZOMBIE_CREATION_PROBABILITY = 0.05;
    // The probability that a human will be created in any given grid position.
    private static final double HUMAN_CREATION_PROBABILITY = 0.08;

    // Lists of creatures in the field. Separate lists are kept for ease of
    // iteration.
    //private List<Human> humans;
    //private List<Vampire> vampires;
    //private List<Zombie> zombies;
    // The current state of the field.
    private Field field;
    // The current step of the simulation.
    private int step;
    // A graphical view of the simulation.
    private SimulatorView view;

    // Default health points for our creatures
    private static final int HP_HUMANS = 100;
    private static final int HP_VAMPIRES = 150;
    private static final int HP_ZOMBIES = 30;
    // List of characters currently in the game
    private ArrayList<Character> characterList;


    /**
     * Construct a simulation field with default size.
     */
    public Simulator() {
        this(DEFAULT_DEPTH, DEFAULT_WIDTH);
    }

    /**
     * Create a simulation field with the given size.
     * 
     * @param depth
     *            Depth of the field. Must be greater than zero.
     * @param width
     *            Width of the field. Must be greater than zero.
     */
    public Simulator(int depth, int width) {
        if (width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            depth = DEFAULT_DEPTH;
            width = DEFAULT_WIDTH;
        }

        /*humans = new ArrayList<Human>();
        zombies = new ArrayList<Zombie>();
        vampires = new ArrayList<Vampire>();*/
        characterList = new ArrayList<Character>();
        field = new Field(depth, width);

        // Create a view of the state of each location in the field.
        view = new SimulatorView(depth, width);
        view.setColor(Human.class, Color.orange);
        view.setColor(Zombie.class, Color.blue);
        view.setColor(Vampire.class, Color.green);

        // Setup a valid starting point.
        reset();
    }

    /**
     * Run the simulation from its current state for a reasonably long period,
     * e.g. 500 steps.
     */
    public void runLongSimulation() {
        simulate(500);
    }

    /**
     * Run the simulation from its current state for the given number of steps.
     * Stop before the given number of steps if it ceases to be viable.
     * 
     * @param numSteps
     *            The number of steps to run for.
     */
    public void simulate(int numSteps) {
        for (int step = 1; step <= numSteps && view.isViable(field); step++) {
            simulateOneStep();
        }
    }

    /**
     * Run the simulation from its current state for a single step. Iterate over
     * the whole field updating the state of each fox and rabbit.
     */
    public void simulateOneStep() {
        step++;

        /*
        // Let all rabbits act.
        for (Iterator<Human> it = humans.iterator(); it.hasNext();) {
            Human human = it.next();
            rabbit.run(newRabbits);
            if (!rabbit.isAlive()) {
                it.remove();
            }
        }

        // Let all foxes act.
        for (Iterator<Fox> it = foxes.iterator(); it.hasNext();) {
            Fox fox = it.next();
            fox.hunt(newFoxes);
            if (!fox.isAlive()) {
                it.remove();
            }
        }*/
        for (int i = 0; i < characterList.size(); ++i) {
            Character c = characterList.get(i);
            Character encountered = characterList.get((i+1)%(characterList.size()));
            c.encounterCharacter(encountered);
        }
        // Dead characters are removed from the character list
        // Need an iterator for removing objects while looping over a collection
        for (Iterator<Character> it = characterList.iterator(); it.hasNext();) {
            Character c = it.next();
            if (c.getHealthPoints() <= 0) {
                it.remove();
            }
        }
        // Each vampire (if he is thirsty) bites the first Human in the list
        // who has not been bitten yet
        for (Character c1 : characterList) {
            if ((c1 instanceof Vampire) && ((Vampire) c1).getIsThirsty()) {
                Vampire v = (Vampire) c1;

                // Find the first human in the list, and bite him
                boolean hasBitten = false;
                for (Character c2 : characterList) {
                    if (!hasBitten && (c2 instanceof Human) && !((Human) c2).getHasBeenBitten()) {
                        v.bite((Human) c2);
                        hasBitten = true;
                    }
                }
            }
        }
        // Humans that have been bitten become vampires
        for (int i=0; i<characterList.size(); ++i) {
            Character c = characterList.get(i);
            if (c instanceof Human && ((Human) c).getHasBeenBitten()) {
                Vampire newVampire = ((Human) c).turnIntoVampire();
                characterList.set(i, newVampire);
                c = null;
            }            
        }
        // Perform end-of-turn actions for all characters (question 4)
        for (int i = 0; i < characterList.size(); ++i) {
            Character c = characterList.get(i);
            c.endOfTurn();
        }

        view.showStatus(step, field);
    }

    /**
     * Perform all game logic for next turn.
     */
    public void nextTurn() {
        // All characters encounter the next character in the list (question 5)
        for (int i = 0; i < characterList.size(); ++i) {
            Character c = characterList.get(i);
            Character encountered = characterList.get((i+1)%(characterList.size()));
            c.encounterCharacter(encountered);
        }
        // Dead characters are removed from the character list
        // Need an iterator for removing objects while looping over a collection
        for (Iterator<Character> it = characterList.iterator(); it.hasNext();) {
            Character c = it.next();
            if (c.getHealthPoints() <= 0) {
                it.remove();
            }
        }
        // Each vampire (if he is thirsty) bites the first Human in the list
        // who has not been bitten yet
        for (Character c1 : characterList) {
            if ((c1 instanceof Vampire) && ((Vampire) c1).getIsThirsty()) {
                Vampire v = (Vampire) c1;

                // Find the first human in the list, and bite him
                boolean hasBitten = false;
                for (Character c2 : characterList) {
                    if (!hasBitten && (c2 instanceof Human) && !((Human) c2).getHasBeenBitten()) {
                        v.bite((Human) c2);
                        hasBitten = true;
                    }
                }
            }
        }
        // Humans that have been bitten become vampires
        for (int i=0; i<characterList.size(); ++i) {
            Character c = characterList.get(i);
            if (c instanceof Human && ((Human) c).getHasBeenBitten()) {
                Vampire newVampire = ((Human) c).turnIntoVampire();
                characterList.set(i, newVampire);
                c = null;
            }            
        }
        // Perform end-of-turn actions for all characters (question 4)
        for (int i = 0; i < characterList.size(); ++i) {
            Character c = characterList.get(i);
            c.endOfTurn();
        }
    }

    /**
     * Reset the simulation to a starting position.
     */
    public void reset() {
        step = 0;
        /*humans.clear();
        zombies.clear();
        vampires.clear();*/
        characterList.clear();
        populate();

        // Show the starting state in the view.
        view.showStatus(step, field);
    }

    /**
     * Randomly populate the field with foxes and rabbits.
     */
    private void populate() {
        Random rand = Randomizer.getRandom();
        field.clear();
        for (int row = 0; row < field.getDepth(); row++) {
            for (int col = 0; col < field.getWidth(); col++) {
                if (rand.nextDouble() <= VAMPIRE_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    String s = "Vampire" + row + col;
                    Vampire vampire = new Vampire(s, HP_VAMPIRES, location, field);
                    characterList.add(vampire);
                } else if (rand.nextDouble() <= ZOMBIE_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    String s = "Zombie" + row + col;
                    Zombie zombie = new Zombie(s, HP_ZOMBIES, location, field);
                    characterList.add(zombie);
                } else if (rand.nextDouble() <= HUMAN_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    String s = "Human" + row + col;
                    Human human = new Human(s, HP_HUMANS, location, field);
                    characterList.add(human);
                }
                // else leave the location empty.
            }
        }
    }


    /**
     * @return the number of human characters currently in the game
     */
    public int nbHumansAlive() {
        // Need to iterate through the list of characters
        // and count the number of humans
        int nbHumans = 0;
        for (Character character : characterList) {
            if (character instanceof Human) {
                nbHumans++;
            }
        }
        return nbHumans;
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        // Game initialization
        Simulator sim = new Simulator();
        sim.simulateOneStep();
        /*System.out.println("Game starts with " + sim.nbHumansAlive() + " humans!");
        // Iterate until no alive human remains
        while (sim.nbHumansAlive() > 0) {
            sim.nextTurn();
        }
        System.out.println("All humans have been eaten!");*/
    }
    /**
     * Generate a pseudo-random boolean.
     * @return pseudo-random boolean
     */
    public static boolean GenerateRandomBoolean() {
        Random random = new Random();
        return random.nextBoolean();
    }




    /**
     * Initialize game.
     */
    /*public void init() {
        // Create characters
        Character h1 = new Human("Human 1", HP_HUMANS);
        Character h2 = new Human("Human 2", HP_HUMANS);
        Character v1 = new Vampire("Vampire 1", HP_VAMPIRES);
        Character v2 = new Vampire("Vampire 2", HP_VAMPIRES);
        Character z1 = new Zombie("Zombie 1", HP_ZOMBIES);
        MadZombie mz1 = new MadZombie("MadZombie 1", HP_ZOMBIES);
        // Add characters to the list
        characterList = new ArrayList<Character>();
        characterList.add(h1);
        characterList.add(h2);
        characterList.add(v1);
        characterList.add(v2);
        characterList.add(z1);
        characterList.add(mz1);
    }*/
}